/********************************************************************************
 * @file    bl0942.c
 * @brief   bl0942 通讯
 * 
 * @author  nzttj
 * @date    2025-02-13
 * @version 1.0
 * 
 * Copyright (C) 2025 nzttj
 * 
 * 本软件使用 GNU 通用公共许可证 (GPL) 第 3 版授权。
 * 
 * 您可以自由使用、修改、分发本软件，但必须遵守以下条件：
 * 1. 源代码必须开放，且您修改后的代码也必须以 GPL 许可证发布。
 * 2. 发布或分发本软件时，必须附带本许可证，并提供源代码或获取源代码的方式。
 * 
 * 本软件按“原样”提供，不附带任何担保，使用风险由用户自行承担。
 * 
 ********************************************************************************/

#include "bl0942.h"
#include "hal_uart.h"

#include <stdint.h>
#include <inttypes.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "stream_buffer.h"
#include "freertos/timers.h"

#define BL0942_UART_NUM     (0)
#define BL0942_UART_BAUD    (4800)

// 全电参数数据包长度
#define BL0942_PACKAGE_LEN  (23)
// 全电参数数据包包头
#define BL0942_START_BYTE   (0x55)

#define BL0942_RX_BUFF_LEN  (512)

#define V_REF   (1.218)         // BL0942 内部基准电压
#define RL      (1.0)           // 1mΩ 采样电阻
#define R1      (0.51)          // 0.51KΩ
#define R2      (390 * 5)       // 390KΩ × 5

typedef struct
{
    uint32_t cf_revp_f : 1;     // 有功脉冲CF能量反向指示，负能量时置1
    uint32_t creep_f : 1;       // 有功功率小于有功防潜动值时置 1
    uint32_t : 6;               // 保留
    uint32_t i_zx_lth_f : 1;    // 电流输出过零指示
    uint32_t v_zx_lth_f : 1;    // 电压输出过零指示
    uint32_t : 14;              // 保留
} electrical_status_t;

typedef struct
{
    uint32_t i_rms;  // 电流有效值
    uint32_t v_rms;  // 电压有效值
    uint32_t i_fast; // 快速电流
    uint32_t watt;   // 有功功率
    uint32_t cf_cnt; // 有功电能脉冲计数
    uint32_t freq;   // 线电压频率
    uint32_t status; // 状态寄存器
} electrical_package_t;

static const char *TAG = "bl0942";
static StreamBufferHandle_t s_bl0942_buff;
static TimerHandle_t s_bl0942_data_req_timer;
static electrical_data_t s_electrical_data = {0};
static bl0942_data_callback_t s_bl0942_data_callback = NULL;

static void _bl0942_data_process(char *data)
{
    // 数据包示例：55 70 27 01 32 4F 38 7D 23 02 9D 62 00 00 00 00 3E 4E 00 00 01 00 D3
    electrical_package_t package = {0};

    package.i_rms = data[1] | (data[2] << 8) | (data[3] << 16);
    package.v_rms = data[4] | (data[5] << 8) | (data[6] << 16);
    package.i_fast = data[7] | (data[8] << 8) | (data[9] << 16);
    package.watt = data[10] | (data[11] << 8) | (data[12] << 16);
    package.cf_cnt = data[13] | (data[14] << 8) | (data[15] << 16);
    package.freq = data[16] | (data[17] << 8) | (data[18] << 16);
    package.status = data[19] | (data[20] << 8) | (data[21] << 16);

    // 有功功率为负数时，转换为补码
    if (package.watt & 0x800000)  // 检查最高位是否为1（bit 23）
    {
        package.watt = (~package.watt + 1) & 0xFFFFFF;  // 取补码
    }

    ESP_LOGI(TAG, "BL0942 Data:");
    ESP_LOGI(TAG, "Effective Current Value (I_RMS): %u", package.i_rms);
    ESP_LOGI(TAG, "Effective Voltage Value (V_RMS): %u", package.v_rms);
    ESP_LOGI(TAG, "Fast Current (I_FAST_RMS): %u", package.i_fast);
    ESP_LOGI(TAG, "Active Power (WATT): %u", package.watt);
    ESP_LOGI(TAG, "Energy Pulse Count (CF_CNT): %u", package.cf_cnt);
    ESP_LOGI(TAG, "Grid Frequency (FREQ): %u Hz", package.freq);
    ESP_LOGI(TAG, "Status Register (STATUS): 0x%02X", package.status);

    electrical_status_t *status = (electrical_status_t *)&package.status;
    // 1. 检测功率方向
    if (status->cf_revp_f)
    {
        ESP_LOGW(TAG, "Reverse Power Detection (Power Backflow, Ignored)");
    }
    // 2. 检测电压过零
    if (status->v_zx_lth_f)
    {
        ESP_LOGW(TAG, "Voltage Zero Crossing Detection Failed (Possible Power Outage or Line Abnormality)");
    }
    // 3. 检测电流过零
    if (status->i_zx_lth_f)
    {
        ESP_LOGW(TAG, "Current Zero Crossing Detection Failed (Possible Disconnection or No Load)");
    }
    // 4. 检测防潜动
    if (status->creep_f)
    {
        ESP_LOGW(TAG, "Anti-Creep Detection (Possible Disconnection or No Load)");
    }

    // 电流 单位：A
    s_electrical_data.current = (package.i_rms * V_REF) / (305978.0 * RL);
    // 电压 单位：V
    s_electrical_data.voltage = (package.v_rms * V_REF * (R2 + R1)) / (73989.0 * R1 * 1000.0);
    // 功率 单位：W
    s_electrical_data.power = (package.watt * V_REF * V_REF * (R2 + R1)) / (3537.0 * RL * R1 * 1000.0);
    // 计算电能，单位：KWh（度）
    s_electrical_data.kwh_total = (1638.4 * 256 * V_REF * V_REF * (R2 + R1) * package.cf_cnt) /
                                  (3600000.0 * 3537.0 * RL * R1 * 1000.0);

    if (s_bl0942_data_callback != NULL)
    {
        s_bl0942_data_callback(&s_electrical_data);
    }

    ESP_LOGI(TAG, "Current (A): %" PRIu32 "", (uint32_t)(s_electrical_data.current * 1000));
    ESP_LOGI(TAG, "Voltage (V): %" PRIu32 "", (uint32_t)(s_electrical_data.voltage * 1000));
    ESP_LOGI(TAG, "Power (W): %" PRIu32 "", (uint32_t)(s_electrical_data.power * 1000));
    ESP_LOGI(TAG, "Energy (Wh) - CF_CNT: %" PRIu32 "", (uint32_t)(s_electrical_data.kwh_total * 1000));
}

// 计算checksum的函数
uint8_t _calculate_checksum(const uint8_t *packet, int length)
{
    uint8_t checksum = 0x58 + packet[0]; // 首字节为0x55，额外加上0x58（根据公式）

    for (int i = 1; i < length; i++)
    { // 忽略末字节checksum
        checksum += packet[i];
    }
    checksum &= 0xff; // 确保checksum为8位

    return ~checksum; // 按位取反
}

static void _bl0942_data_parse_task(void *param)
{
    char dataPackage[BL0942_PACKAGE_LEN];
    char receivedByte;
    int packageIndex = 0;
 
    for (;;)
    {
        xStreamBufferReceive(s_bl0942_buff, &receivedByte, sizeof(receivedByte), portMAX_DELAY);

        // 检查是否是数据包的起始字节
        if (packageIndex == 0 && receivedByte != BL0942_START_BYTE)
        {
            continue;
        }
        // 将接收到的字节存储到数据包中
        dataPackage[packageIndex++] = receivedByte;
        // 检查是否收到了完整的数据包
        if (packageIndex == BL0942_PACKAGE_LEN)
        {
            // 重置索引以便接收下一个数据包
            packageIndex = 0;
            // 计算数据包（不包括最后一个字节，即校验和）的校验和
            uint8_t calculatedChecksum = _calculate_checksum((const uint8_t *)dataPackage, BL0942_PACKAGE_LEN - 1);
            // 验证校验和（使用数据包的最后一个字节）
            if (calculatedChecksum == dataPackage[BL0942_PACKAGE_LEN - 1])
            {
                // 校验和正确，处理数据包
                _bl0942_data_process(dataPackage);
            }
            else
            {
                ESP_LOGE(TAG, "checksum error!");
                // for (size_t i = 0; i < BL0942_PACKAGE_LEN; i++)
                // {
                //     printf("%02x ", dataPackage[i]);
                // }
                // printf("\n");
                // 校验和不正确，记录错误或重置状态
                // 可选：记录错误或重置数据包缓冲区
            }
        }
    }
}

electrical_data_t *bl0942_get_electrical_data(void)
{
    return &s_electrical_data;
}

void bl0942_data_callback_register(bl0942_data_callback_t callback)
{
    s_bl0942_data_callback = callback;
}

static int _bl0942_recv_data_cb(char *data, int len)
{
    xStreamBufferSend(s_bl0942_buff, data, len, pdMS_TO_TICKS(10));
    return 0;
}

int bl0942_full_electrical_data_req(void)
{
    char data[2] = {0x58, 0xaa};
    hal_uart_write_bytes(BL0942_UART_NUM, data, sizeof(data));
    return 0;
}

static void _full_electrical_data_req(TimerHandle_t xTimer)
{
    bl0942_full_electrical_data_req();
}

int bl0942_init(void)
{
    // 初始化数据缓存
    s_bl0942_buff = xStreamBufferCreate(BL0942_RX_BUFF_LEN, 1);
    if (!s_bl0942_buff)
    {
        ESP_LOGE(TAG, "create rx buff failed!");
        return -1;
    }
    // 初始化数据处理任务
    xTaskCreate(_bl0942_data_parse_task, "bl0942", 2048, NULL, 12, NULL);
    // 初始化通讯接口
    hal_uart_init(BL0942_UART_NUM, BL0942_UART_BAUD, (uart_rx_cb_t)_bl0942_recv_data_cb);
    // 初始化全电参数数据请求定时器
    s_bl0942_data_req_timer = xTimerCreate("data_req", pdMS_TO_TICKS(1000), pdTRUE, NULL, _full_electrical_data_req);
    xTimerStart(s_bl0942_data_req_timer, 0);

    return 0;
}


